package com.songhuan.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.songhuan.web.dto.*;
import com.songhuan.web.dto.query.*;
import com.songhuan.web.entity.*;
import com.songhuan.web.mapper.*;
import com.songhuan.web.service.*;
import com.songhuan.web.tools.dto.*;
import com.songhuan.web.tools.Extension;
import com.songhuan.web.tools.exception.CustomException;
import com.songhuan.web.dto.*;
import com.songhuan.web.dto.query.CheckRecordPagedInput;
import com.songhuan.web.entity.*;
import com.songhuan.web.mapper.*;
import com.songhuan.web.service.CheckRecordService;
import com.songhuan.web.tools.dto.PagedResult;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.SneakyThrows;
import com.songhuan.web.tools.*;

/**
 * 打卡记录功能实现类
 */
@Service
public class CheckRecordServiceImpl extends ServiceImpl<CheckRecordMapper, CheckRecord> implements CheckRecordService {

    /**
     * 操作数据库AppUser表mapper对象
     */
    @Autowired
    private AppUserMapper _AppUserMapper;
    /**
     * 操作数据库的CheckRecord表mapper对象
     */
    @Autowired
    private CheckRecordMapper _CheckRecordMpper;


    @Autowired
    private OrganizationMapper _OrganizationMapper;

    /**
     * 操作数据库的Organization表mapper对象
     */
    @Autowired
    private OrganizationMapper _OrganizationMpper;

    /**
     * 操作数据库的UserFace表mapper对象
     */
    @Autowired
    private UserFaceMapper _UserFaceMpper;

    /**
     * 操作数据库的CheckSetting表mapper对象
     */
    @Autowired
    private CheckSettingMapper _CheckSettingMpper;
    /**
     * 操作数据库的OrganizationRelativeUser表mapper对象
     */
    @Autowired
    private OrganizationRelativeUserMapper _OrganizationRelativeUserMpper;
    /**
     * 操作数据库的ReCheck表mapper对象
     */
    @Autowired
    private ReCheckMapper _ReCheckMpper;

    /**
     * 打卡记录分页查询
     */
    @SneakyThrows
    @Override
    public PagedResult<CheckRecordDto> List(CheckRecordPagedInput input) {


        //声明一个支持打卡记录查询的(拉姆达)表达式
        LambdaQueryWrapper<CheckRecord> queryWrapper = Wrappers.<CheckRecord>lambdaQuery()
                .eq(input.getId() != null && input.getId() != 0, CheckRecord::getId, input.getId())
                .eq(input.getCreatorId() != null, CheckRecord::getCreatorId, input.getCreatorId());
        //如果前端搜索传入查询条件则拼接查询条件
        if (input.getUserId() != null) {
            queryWrapper = queryWrapper.eq(CheckRecord::getUserId, input.getUserId());
        }
        if (input.getOrganizationId() != null) {
            queryWrapper = queryWrapper.eq(CheckRecord::getOrganizationId, input.getOrganizationId());
        }
        if (input.getLongitudeStartRange() != null) {
            queryWrapper = queryWrapper.gt(CheckRecord::getLongitude, input.getLongitudeStartRange());
        }
        if (input.getLongitudeEndRange() != null) {
            queryWrapper = queryWrapper.lt(CheckRecord::getLongitude, input.getLongitudeEndRange());
        }
        if (input.getLatitudeStartRange() != null) {
            queryWrapper = queryWrapper.gt(CheckRecord::getLatitude, input.getLatitudeStartRange());
        }
        if (input.getLatitudeEndRange() != null) {
            queryWrapper = queryWrapper.lt(CheckRecord::getLatitude, input.getLatitudeEndRange());
        }
        if (input.getCheckDateRange() != null) {
            queryWrapper = queryWrapper.apply("Date(CheckDate) = {0}", input.getCheckDateRange());
        }
        if (input.getBeginTimeRange() != null && input.getBeginTimeRange().stream().count() > 1) {
            queryWrapper = queryWrapper.lt(CheckRecord::getBeginTime, input.getBeginTimeRange().get(1));
            queryWrapper = queryWrapper.gt(CheckRecord::getBeginTime, input.getBeginTimeRange().get(0));
        }
        if (input.getEndTimeRange() != null && input.getEndTimeRange().stream().count() > 1) {
            queryWrapper = queryWrapper.lt(CheckRecord::getEndTime, input.getEndTimeRange().get(1));
            queryWrapper = queryWrapper.gt(CheckRecord::getEndTime, input.getEndTimeRange().get(0));
        }
        if (input.getCheckSettingId() != null) {
            queryWrapper = queryWrapper.eq(CheckRecord::getCheckSettingId, input.getCheckSettingId());
        }

        //按创建时间从大到小排序 最新的显示在最前面
        queryWrapper = queryWrapper.orderByDesc(CheckRecord::getCreationTime);
        //构建一个分页查询的model
        Page<CheckRecord> page = new Page<>(input.getPage(), input.getLimit());


        //从数据库进行分页查询获取打卡记录数据
        IPage<CheckRecord> pageRecords = _CheckRecordMpper.selectPage(page, queryWrapper);

        //获取所有满足条件的数据行数
        Long totalCount = _CheckRecordMpper.selectCount(queryWrapper);

        //把CheckRecord实体转换成CheckRecord传输模型
        List<CheckRecordDto> items = Extension.copyBeanList(pageRecords.getRecords(), CheckRecordDto.class);
        for (CheckRecordDto item : items) {

            //查询出关联的创建用户信息
            AppUserDto CreatorAppUserDTO = new AppUserDto();
            AppUser CreatorAppUserEntity = _AppUserMapper.selectOne(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getId, item.getCreatorId()));
            if (CreatorAppUserEntity != null) {
                BeanUtils.copyProperties(CreatorAppUserDTO, CreatorAppUserEntity);
                item.setCreatorAppUserDto(CreatorAppUserDTO);
            }


            //查询出关联的Organization表信息
            OrganizationDto OrganizationDTO = new OrganizationDto();
            Organization OrganizationEntity = _OrganizationMapper.selectOne(Wrappers.<Organization>lambdaQuery().eq(Organization::getId, item.getOrganizationId()));
            if (OrganizationEntity != null) {
                BeanUtils.copyProperties(OrganizationDTO, OrganizationEntity);
                item.setOrganizationDto(OrganizationDTO);
            }


            //查询出关联的AppUser表信息
            AppUserDto UserDTO = new AppUserDto();
            AppUser UserEntity = _AppUserMapper.selectOne(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getId, item.getUserId()));
            if (UserEntity != null) {
                BeanUtils.copyProperties(UserDTO, UserEntity);
                item.setUserDto(UserDTO);
            }

        }

        //返回一个分页结构给前端
        return PagedResult.GetInstance(items, totalCount);

    }

    /**
     * 单个打卡记录查询
     */
    @SneakyThrows
    @Override
    public CheckRecordDto Get(CheckRecordPagedInput input) {
        if (input.getId() == null) {
            return new CheckRecordDto();
        }
        PagedResult<CheckRecordDto> pagedResult = List(input);
        return pagedResult.getTotalCount() > 0 ? pagedResult.getItems().stream().findFirst().get() : new CheckRecordDto();
    }

    /**
     * 打卡记录创建或者修改
     */
    @SneakyThrows
    @Override
    public CheckRecordDto CreateOrEdit(CheckRecordDto input) {

        //声明一个打卡记录实体
        CheckRecord CheckRecord = new CheckRecord();

        //把前端传入的input参数拷贝到打卡记录实体
        BeanUtils.copyProperties(CheckRecord, input);

        //调用数据库的增加或者修改方法
        saveOrUpdate(CheckRecord);

        //定义一个返回给前端的打卡记录传输模型
        CheckRecordDto CheckRecordDto = new CheckRecordDto();

        //同理把操作的打卡记录实体拷贝给打卡记录传输模型
        BeanUtils.copyProperties(CheckRecordDto, CheckRecord);

        //把传输模型返回给前端
        return CheckRecordDto;
    }


    /**
     * 打卡记录创建或者修改
     */
    @SneakyThrows
    @Override
    public void CheckMyStatus(CheckRecordDto input) {

        if (input.getUserId() == null) {
            throw new CustomException("用户ID不能为空");
        }

        //查询用户信息
        AppUser appUser = _AppUserMapper.selectById(input.getUserId());
        if (Extension.isNullOrZero(appUser.getOrganizationId())) {
            throw new CustomException("请先关联自己班级");
        }
        //查询用户人脸数量
        Long faceCount = _UserFaceMpper.selectCount(Wrappers.<UserFace>lambdaQuery().eq(UserFace::getUserId, input.getUserId()));
        if (faceCount < 2) {
            throw new CustomException("请先完成人脸认证");
        }

    }

    /**
     * 得到打卡状态
     */
    @SneakyThrows
    @Override
    public CheckStatusInfo GetClockInfo(CheckRecordDto input) {
        CheckStatusInfo result = new CheckStatusInfo();
        LocalDate now = LocalDate.now();
        //查询公司班级
        Organization organization = _OrganizationMapper.selectById(input.getOrganizationId());
        //转换成dto
        OrganizationDto OrganizationDto = new OrganizationDto();
        BeanUtils.copyProperties(OrganizationDto, organization);
        result.setOrganizationDto(OrganizationDto);

        if (organization == null) {
            throw new CustomException("公司班级不存在");
        }
        //查询公司所有的打卡配置信息
        List<CheckSetting> checkSettings = _CheckSettingMpper.selectList(Wrappers.<CheckSetting>lambdaQuery().eq(CheckSetting::getOrganizationId, input.getOrganizationId()));


        //得到用户的打卡记录
        List<CheckRecord> checkRecords = _CheckRecordMpper.selectList(Wrappers.<CheckRecord>lambdaQuery().eq(CheckRecord::getOrganizationId, input.getOrganizationId()).eq(CheckRecord::getUserId, input.getUserId()));
        //得到用户今天的打卡记录
        List<CheckRecord> todayCheckRecords = checkRecords.stream().filter(x -> x.getCheckDate().toLocalDate().equals(now)).toList();
        //转换成dto
        List<CheckRecordDto> CheckRecordDtos = Extension.copyBeanList(todayCheckRecords, CheckRecordDto.class);
        result.setCheckRecordDto(CheckRecordDtos);


        //得到当前日期在打卡的开始时间和结束时间之间的记录
        CheckSetting checkSetting = null;
        for (CheckSetting setting : checkSettings) {
            if (setting.getBeginDate().equals(now) || setting.getEndDate().equals(now)) {
                checkSetting = setting;
                break;
            }
            if (setting.getBeginDate().isBefore(now) && setting.getEndDate().isAfter(now)) {
                checkSetting = setting;
                break;
            }

        }


        //得到当前时间点
        LocalDateTime nowTime = LocalDateTime.now();
        //当前时间点是否在打卡时间段内
        if (checkSetting != null) {
            String[] AmBeginHMS = checkSetting.getAmBeginHMS().split(":");
            LocalDateTime checkAmBeginTime = LocalDate.now().atTime(Integer.parseInt(AmBeginHMS[0]), Integer.parseInt(AmBeginHMS[1]), 0);
            String[] AmEndHMS = checkSetting.getAmBeginHMS().split(":");
            LocalDateTime checkAmEndTime = LocalDate.now().atTime(Integer.parseInt(AmEndHMS[0]), Integer.parseInt(AmEndHMS[1]), 0);

            String[] PmBeginHMS = checkSetting.getPmBeginHMS().split(":");
            LocalDateTime checkPmBeginTime = LocalDate.now().atTime(Integer.parseInt(PmBeginHMS[0]), Integer.parseInt(PmBeginHMS[1]), 0);
            String[] PmEndHMS = checkSetting.getPmEndHMS().split(":");
            if (PmEndHMS[0].equals("24")) {
                PmEndHMS[0] = "23";
                PmEndHMS[1] = "59";
            }
            LocalDateTime checkPmEndTime = LocalDate.now().atTime(Integer.parseInt(PmEndHMS[0]), Integer.parseInt(PmEndHMS[1]), 0);


            if ((nowTime.isAfter(checkAmBeginTime) && nowTime.isBefore(checkAmEndTime))) {
                result.setIsCheckTime(true);

            }
            if ((nowTime.isAfter(checkPmBeginTime) && nowTime.isBefore(checkPmEndTime))) {
                result.setIsCheckTime(true);
            }


        }

        result.setCheckCount(todayCheckRecords.size());


        return result;

    }

    /**
     * 进行打卡
     */
    @SneakyThrows
    @Override
    public void Check(CheckRecordDto input) {

        LocalDate now = LocalDate.now();
        String hourAndMinute = String.format("%02d", LocalDateTime.now().getHour()) + ":" + String.format("%02d", LocalDateTime.now().getMinute());
        //查询公司班级
        Organization organization = _OrganizationMapper.selectById(input.getOrganizationId());
        //转换成dto
        OrganizationDto OrganizationDto = new OrganizationDto();
        BeanUtils.copyProperties(OrganizationDto, organization);

        if (organization == null) {
            throw new CustomException("公司班级不存在");
        }

        //得到当前时间点
        LocalDateTime nowTime = LocalDateTime.now();
        CheckRecord checkRecord = new CheckRecord();
        checkRecord.setOrganizationId(input.getOrganizationId());
        checkRecord.setUserId(input.getUserId());
        checkRecord.setCheckDate(now.atTime(0, 0, 0));
        checkRecord.setCheckDate(nowTime);
        checkRecord.setLatitude(input.getLatitude());
        checkRecord.setLongitude(input.getLongitude());
        checkRecord.setFaceImageUrl(input.getFaceImageUrl());
        checkRecord.setCheckSettingId(input.getCheckSettingId());
        checkRecord.setCheckType(input.getCheckType());
        // 默认签到成功，如果时间不在考勤时间范围内，视为补卡
        //查询班级所有的打卡配置信息
        CheckSetting checkSetting = _CheckSettingMpper.selectById(input.getCheckSettingId());
        if (checkSetting.getAmEndHMS().compareTo(hourAndMinute) < 0) {
            checkRecord.setCheckStatus(2);
        } else {
            checkRecord.setCheckStatus(1);
        }

        saveOrUpdate(checkRecord);

    }

    /**
     * 获取签到信息
     * @return
     */
    public CheckSetting pullCheckSetting(CheckRecordDto input) {

        // 查询当前的考勤设置
        LocalDate now = LocalDate.now();
        String hourAndMinute = String.format("%02d", LocalDateTime.now().getHour()) + ":" + String.format("%02d", LocalDateTime.now().getMinute());
        //查询班级所有的打卡配置信息
        List<CheckSetting> checkSettings = _CheckSettingMpper.selectList(Wrappers.<CheckSetting>lambdaQuery()
                .eq(CheckSetting::getOrganizationId, input.getOrganizationId())
                .le(CheckSetting::getBeginDate, now)
                .ge(CheckSetting::getEndDate, now)
        );

        CheckSetting localCheckSetting = null;
        int lastTime = 0;
        for (CheckSetting setting : checkSettings) {

            // 查询是否有进行考勤打卡
            CheckRecord checkRecord = _CheckRecordMpper.selectOne(Wrappers.<CheckRecord>lambdaQuery()
                    .eq(CheckRecord::getCheckSettingId, setting.getId())
                    .apply("DATE(CheckDate) = {0}", now)
                    .eq(CheckRecord::getUserId, input.getUserId())
            );

            // 判断最近的打卡时间
            if (setting.getAmBeginHMS().compareTo(hourAndMinute) <= 0 && setting.getAmEndHMS().compareTo(hourAndMinute) >= 0) {
                if (checkRecord != null) {
                    // 代表已经签到
                    setting.setCheckStatus(1);
                } else {
                    // 代表未签到
                    setting.setCheckStatus(0);
                }
                localCheckSetting = setting;
                break;
            } if (setting.getAmBeginHMS().compareTo(hourAndMinute) >= 0) {
                if (checkRecord != null) {
                    // 代表已经签到
                    setting.setCheckStatus(1);
                } else {
                    // 代表未签到
                    setting.setCheckStatus(0);
                }
                localCheckSetting = setting;
                break;
            } else {
                // 代表迟到，默认5分钟
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
                String plusTime = LocalTime.parse(setting.getAmEndHMS(), formatter).plus(Duration.ofMinutes(5)).format(formatter);

                if (checkRecord == null) {
                    if (plusTime.compareTo(hourAndMinute) >= 0) {
                        // 5分钟内刚迟到
                        // 代表已经签到
                        setting.setCheckStatus(2);
                        localCheckSetting = setting;
                        break;
                    }
                }
            }

        }
        return localCheckSetting;
    }

    /**
     * 修改考勤状态
     */
    @Override
    public void ChangeCheckStatus(AppUserCheckStat input) {

        // 默认是不存在的，直接插入到考勤记录表中
        CheckRecord checkRecord = new CheckRecord();
        checkRecord.setCheckStatus(input.getCheckStatus());
        checkRecord.setUserId(input.getId());
        checkRecord.setCheckSettingId(input.getCheckSettingId());
        checkRecord.setOrganizationId(input.getOrganizationId());
        checkRecord.setCheckDate(LocalDateTime.now());
        checkRecord.setCheckStatus(input.getCheckStatus());
        saveOrUpdate(checkRecord);
    }


    /**
     * 得到我的打卡记录
     */
    @SneakyThrows
    @Override
    public List<Map<String, Object>> GetCheckList(CheckRecordDto input) {
        List<Map<String, Object>> rs = new ArrayList<>();
        //得到我的个人信息
        AppUser appUser = _AppUserMapper.selectById(input.getUserId());

        //得到我进入班级的最新时间
        List<OrganizationRelativeUser> organizationRelativeUsers = _OrganizationRelativeUserMpper.selectList(Wrappers.<OrganizationRelativeUser>lambdaQuery().eq(OrganizationRelativeUser::getUserId, input.getUserId()));
        //查询审核通过的
        organizationRelativeUsers = organizationRelativeUsers.stream().filter(x -> x.getAuditStatus() == Enums.AuditStatus.审核通过.index()).toList();
        if (organizationRelativeUsers.size() == 0) {
            return rs;
        }
        LocalDateTime creationTime = organizationRelativeUsers.get(0).getCreationTime();


        //得到当前班级的所有配置记录
        List<CheckSetting> checkSettings = _CheckSettingMpper.selectList(Wrappers.<CheckSetting>lambdaQuery().eq(CheckSetting::getOrganizationId, appUser.getOrganizationId()));
        //得到配置开始时间小于creationTime的记录
        checkSettings = checkSettings.stream().filter(x -> x.getBeginDate().isBefore(creationTime.toLocalDate())).toList();
        if (checkSettings.size() == 0) {
            return rs;
        }
        List<LocalDate> dates = new ArrayList<>();
        for (CheckSetting checkSetting : checkSettings) {
            //得到开始日期和结束日期的天数
            long days = checkSetting.getEndDate().toEpochDay() - checkSetting.getBeginDate().toEpochDay();
            for (int i = 0; i < days; i++) {
                if (checkSetting.getBeginDate().plusDays(i).isBefore(LocalDate.now())) {
                    dates.add(checkSetting.getBeginDate().plusDays(i));
                }
            }

        }
        //对相同的天数去重
        dates = dates.stream().distinct().toList();


        //得到我的所有打卡记录在这个班级下的
        List<CheckRecord> checkRecords = _CheckRecordMpper.selectList(Wrappers.<CheckRecord>lambdaQuery().eq(CheckRecord::getUserId, input.getUserId()).eq(CheckRecord::getOrganizationId, appUser.getOrganizationId()));

        for (LocalDate date : dates) {
            Map<String, Object> item = new HashMap<>();
            item.put("date", date);
            //得到打卡的记录
            List<CheckRecord> checkRecordsByDate = checkRecords.stream().filter(x -> x.getCheckDate().equals(date)).toList();

            if (checkRecordsByDate.size() == 2) {
                item.put("content", "已打卡");
            }
            if (checkRecordsByDate.size() == 1) {
                item.put("content", "部分打卡");
            }
            if (checkRecordsByDate.size() == 0) {
                item.put("content", "未打卡");
            }
            rs.add(item);

        }
        return rs;
    }


    /**
     * 得到需要补卡的记录
     */
    @SneakyThrows
    @Override
    public List<Map<String, Object>> GetNeedRepList(CheckRecordDto input) {
        List<Map<String, Object>> rs = new ArrayList<>();
        //得到我的个人信息
        AppUser appUser = _AppUserMapper.selectById(input.getUserId());

        //得到我进入班级的最新时间
        List<OrganizationRelativeUser> organizationRelativeUsers = _OrganizationRelativeUserMpper.selectList(Wrappers.<OrganizationRelativeUser>lambdaQuery().eq(OrganizationRelativeUser::getUserId, input.getUserId()));
        //查询审核通过的
        organizationRelativeUsers = organizationRelativeUsers.stream().filter(x -> x.getAuditStatus() == Enums.AuditStatus.审核通过.index()).toList();
        if (organizationRelativeUsers.size() == 0) {
            return rs;
        }
        LocalDateTime creationTime = organizationRelativeUsers.get(0).getCreationTime();


        //得到当前班级的所有配置记录
        List<CheckSetting> checkSettings = _CheckSettingMpper.selectList(Wrappers.<CheckSetting>lambdaQuery().eq(CheckSetting::getOrganizationId, appUser.getOrganizationId()));
        //得到配置开始时间小于creationTime的记录
        checkSettings = checkSettings.stream().filter(x -> x.getBeginDate().isBefore(creationTime.toLocalDate())).toList();
        if (checkSettings.size() == 0) {
            return rs;
        }
        List<LocalDate> dates = new ArrayList<>();
        for (CheckSetting checkSetting : checkSettings) {
            //得到开始日期和结束日期的天数
            long days = checkSetting.getEndDate().toEpochDay() - checkSetting.getBeginDate().toEpochDay();
            for (int i = 0; i < days; i++) {
                if (checkSetting.getBeginDate().plusDays(i).isBefore(LocalDate.now())) {
                    dates.add(checkSetting.getBeginDate().plusDays(i));
                }
            }

        }
        //对相同的天数去重
        dates = dates.stream().distinct().toList();


        //得到我的补卡记录
        List<ReCheck> reChecks = _ReCheckMpper.selectList(Wrappers.<ReCheck>lambdaQuery().eq(ReCheck::getUserId, input.getUserId()).eq(ReCheck::getOrganizationId, appUser.getOrganizationId()));
        //得到我的所有打卡记录在这个班级下的
        List<CheckRecord> checkRecords = _CheckRecordMpper.selectList(Wrappers.<CheckRecord>lambdaQuery().eq(CheckRecord::getUserId, input.getUserId()).eq(CheckRecord::getOrganizationId, appUser.getOrganizationId()));

        for (LocalDate date : dates) {
            Map<String, Object> item = new HashMap<>();
            item.put("date", date);
            //得到打卡的记录
            List<CheckRecord> checkRecordsByDate = checkRecords.stream().filter(x -> x.getCheckDate().toLocalDate().equals(date)).toList();
            //转换成dto
            List<CheckRecordDto> CheckRecordDtos = Extension.copyBeanList(checkRecordsByDate, CheckRecordDto.class);

            reChecks = reChecks.stream().filter(x -> x.getBeginTime().toLocalDate().equals(date)).toList();
            item.put("repCount", reChecks.size());
            //得到是否存在补卡成功的记录
            var reSuccessChecks = reChecks.stream().filter(x -> x.getAuditStatus() == Enums.AuditStatus.审核通过.index()).toList();
            item.put("reSuccessChecks", reSuccessChecks.size());
            //得到补卡不成功的记录

            List<ReCheckDto> ReCheckDtos = Extension.copyBeanList(reChecks, ReCheckDto.class);

            item.put("reChecks", ReCheckDtos);

            item.put("count", CheckRecordDtos.size());
            rs.add(item);

        }
        return rs;
    }
}
